package com.jet.member.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.jet.core.enums.CommonSource;
import com.jet.core.enums.CommonStatus;
import com.jet.core.enums.IEnum;
import com.jet.core.exception.CustomException;
import com.jet.core.model.vo.PageVo;
import com.jet.core.result.ResultCode;
import com.jet.core.utils.AmountUtil;
import com.jet.core.utils.ExcelUtil;
import com.jet.core.utils.IdUtil;
import com.jet.member.domain.dto.MemberDto;
import com.jet.member.domain.dto.MemberImportDto;
import com.jet.member.domain.dto.MemberTagDto;
import com.jet.member.domain.entity.*;
import com.jet.member.domain.enums.MemberChannel;
import com.jet.member.domain.enums.MemberSex;
import com.jet.member.domain.query.MemberQuery;
import com.jet.member.domain.query.MemberSummaryQuery;
import com.jet.member.domain.vo.*;
import com.jet.member.mapper.MemberMapper;
import com.jet.member.remote.cashier.MemberCashierRemote;
import com.jet.member.remote.cashier.vo.MemberCashierLastOrderVo;
import com.jet.member.remote.cashier.vo.MemberCashierSummaryVo;
import com.jet.member.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jet.merchant.domain.entity.ClerkAccount;
import com.jet.merchant.domain.entity.MerchantStore;
import com.jet.merchant.domain.vo.MerchantStoreVo;
import com.jet.merchant.domain.vo.UserVo;
import com.jet.merchant.service.IClerkAccountService;
import com.jet.merchant.service.IMerchantStoreService;
import com.jet.sms.domain.dto.SmsMemberRegisterDto;
import com.jet.sms.service.SmsSendNoticeService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author author
 * @since 2025-03-02
 */
@Service
@Slf4j
public class MemberServiceImpl extends ServiceImpl<MemberMapper, Member> implements IMemberService {

    @Resource
    private SmsSendNoticeService smsSendNoticeService;

    private final IMemberTimeCardService memberTimeCardService;
    private final IMerchantStoreService merchantStoreService;
    private final MemberCashierRemote memberCashierRemote;
    private final IClerkAccountService clerkAccountService;
    private final IMemberAssetsService memberAssetsService;
    private final IMemberLevelService memberLevelService;
    private final IMemberTagMapService memberTagMapService;
    private final IMemberTagService memberTagService;

    @Lazy
    public MemberServiceImpl(IMemberTimeCardService memberTimeCardService, IMerchantStoreService merchantStoreService, MemberCashierRemote memberCashierRemote, IClerkAccountService clerkAccountService, IMemberAssetsService memberAssetsService, IMemberLevelService memberLevelService, IMemberTagMapService memberTagMapService, IMemberTagService memberTagService) {
        this.memberTimeCardService = memberTimeCardService;
        this.merchantStoreService = merchantStoreService;
        this.memberCashierRemote = memberCashierRemote;
        this.clerkAccountService = clerkAccountService;
        this.memberAssetsService = memberAssetsService;
        this.memberLevelService = memberLevelService;
        this.memberTagMapService = memberTagMapService;
        this.memberTagService = memberTagService;
    }

    @Override
    @Transactional(rollbackFor = CustomException.class)
    public void create(long merchantNo, long storeNo, String accountId, MemberDto dto) {
        create(merchantNo, storeNo, accountId, dto, 0, 0);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Member create(long merchantNo, long storeNo, String accountId, MemberDto dto,
                         long initRecharge, long initIntegral) {
        if (existByPhone(merchantNo, null, dto.getPhone())) {
            throw new CustomException(ResultCode.BAD_REQUEST, "会员手机号已存在");
        }
        Member entity = new Member();
        BeanUtils.copyProperties(dto, entity);
        entity.setId(IdUtil.generateLower());
        entity.setMerchantNo(merchantNo);
        entity.setStoreNo(storeNo);
        entity.setAccountId(accountId);
        super.save(entity);

        memberAssetsService.init(merchantNo, entity.getId(), initRecharge, initIntegral);
        // 修改会员等级
        syncMemberLevel(merchantNo, entity.getId());
        afterMember(entity.getId());
        return entity;
    }

    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void afterMember(String memberId) {
        // 发送短信通知
        MemberInfoVo member = findById(memberId);
        if (member == null) {
            return;
        }
        SmsMemberRegisterDto dto = new SmsMemberRegisterDto();
        dto.setMemberId(memberId);
        dto.setMemberName(member.getName());
        dto.setSex(member.getSex() == 1 ? "先生" : "女士");
        dto.setPhone(member.getPhone());
        dto.setMerchantNo(member.getMerchantNo());
        dto.setStoreNo(member.getStoreNo());
        dto.setStoreName(member.getStoreName());
        dto.setAccountId(member.getAccountId());
        smsSendNoticeService.memberRegisterSend(dto);
    }

    @Override
    @Transactional(rollbackFor = CustomException.class)
    public void memberImport(long merchantNo, long storeNo, String accountId, MultipartFile file) {
        List<MemberImportDto> dtos = ExcelUtil.read(file, MemberImportDto.class);
        if (CollectionUtils.isEmpty(dtos)) {
            throw new CustomException("没有获取到文件内容");
        }
        dtos.forEach(x -> {
            int sex = MemberSex.MAN.getCode();
            if ("女".equals(x.getSex())) {
                sex = MemberSex.WOMAN.getCode();
            }

            MemberDto dto = new MemberDto();
            dto.setPhone(x.getPhone());
            dto.setName(x.getName());
            dto.setSex(sex);
            dto.setBirthDay(x.getBirthDay());
            dto.setRemark("文件导入会员");
            dto.setSource(CommonSource.ADMIN.getCode());
            dto.setChannel(MemberChannel.MANUAL.getCode());
            long initRechage = AmountUtil.parse(x.getRecharge());
            long initIntegral = 0;
            if (x.getIntegral() != null) {
                initIntegral = x.getIntegral();
            }
            Member member = create(merchantNo, storeNo, accountId, dto, initRechage, initIntegral);
            initTags(merchantNo, member.getId(), x.getTags());
        });
    }

    public void initTags(long merchantNo, String memberId, String tags) {
        if (StrUtil.isBlank(tags)) {
            return;
        }
        List<String> tagIds = new ArrayList<>();
        List<String> tagList = Arrays.stream(tags.replaceAll("，", ",").split(",")).collect(Collectors.toList());
        tagList.forEach(x -> {
            MemberTag entity = memberTagService.findByName(merchantNo, x);
            if (entity != null) {
                tagIds.add(entity.getId());
            } else {
                MemberTagDto dto = new MemberTagDto();
                dto.setName(x);
                dto.setStatus(CommonStatus.ENABLED.getCode());
                entity = memberTagService.create(merchantNo, dto);
                if (entity != null) {
                    tagIds.add(entity.getId());
                }
            }
        });
        memberTagMapService.batchSave(memberId, tagIds);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void edit(long merchantNo, long storeNo, String accountId, String id, MemberDto dto) {
        if (!existById(merchantNo, id)) {
            throw new CustomException(ResultCode.BAD_REQUEST, "会员不存在");
        }
        if (existByPhone(merchantNo, id, dto.getPhone())) {
            throw new CustomException("手机号重复");
        }

        Member entity = new Member();
        BeanUtils.copyProperties(dto, entity);
        entity.setId(id);
        super.updateById(entity);
    }

    @Override
    public void editStatus(long merchantNo, long storeNo, String accountId, String id, Integer status) {
        if (!existById(merchantNo, id)) {
            throw new CustomException(ResultCode.BAD_REQUEST, "会员不存在");
        }
        if (IEnum.getByCode(status, CommonStatus.class) == null) {
            throw new CustomException(ResultCode.BAD_REQUEST, "修改状态错误");
        }
        Member entity = new Member();
        entity.setId(id);
        entity.setStatus(status);
        super.updateById(entity);
    }

    @Override
    public void deleteById(String id) {
        super.removeById(id);
    }

    @Override
    public void syncMemberLevel(long merchantNo) {
        List<MemberListVo> members = findAll(merchantNo);
        List<MemberLevel> memberLevels = memberLevelService.findAllByMerchantNo(merchantNo);
        members.forEach(x -> {
            editLevel(merchantNo, x.getId(), memberLevels);
        });
    }

    @Override
    public void syncMemberLevel(long merchantNo, String memberId) {
        List<MemberLevel> memberLevels = memberLevelService.findAllByMerchantNo(merchantNo);
        editLevel(merchantNo, memberId, memberLevels);
    }

    public void editLevel(long merchantNo, String memberId, List<MemberLevel> memberLevels) {
        MemberAssets assets = memberAssetsService.getById(memberId);
        memberLevels.stream()
                .filter(level -> assets.getAccrualIntegral() >= level.getStartIntegral() && assets.getAccrualIntegral() < level.getEndIntegral())
                .findFirst()
                .ifPresent(level -> {
                    Member entity = new Member();
                    entity.setId(memberId);
                    entity.setLevelId(level.getId());
                    super.updateById(entity);
                });
    }

    public boolean existById(long merchantNo, String id) {
        Wrapper<Member> wrapper = new LambdaQueryWrapper<Member>()
                .eq(Member::getMerchantNo, merchantNo)
                .eq(Member::getId, id);
        return super.exists(wrapper);
    }

    public boolean existByPhone(long merchantNo, String id, String phone) {
        Wrapper<Member> wrapper = new LambdaQueryWrapper<Member>()
                .eq(Member::getMerchantNo, merchantNo)
                .eq(Member::getPhone, phone)
                .ne(StrUtil.isNotBlank(id), Member::getId, id);
        return super.exists(wrapper);
    }

    @Override
    public MemberInfoVo findById(String id) {
        Member entity = super.getById(id);
        if (entity == null) {
            return null;
        }
        return convertMemberInfo(entity);
    }

    @Override
    public List<MemberListVo> findAllByIds(long merchantNo, List<String> ids) {
        if (CollUtil.isEmpty(ids)) {
            return Collections.emptyList();
        }
        List<Member> members = super.listByIds(ids);
        if (members.isEmpty()) {
            return Collections.emptyList();
        }
        return convertMemberList(merchantNo, members);
    }

    @Override
    public List<MemberListVo> findAll(long merchantNo) {
        Wrapper<Member> wrapper = new LambdaQueryWrapper<Member>()
                .eq(Member::getMerchantNo, merchantNo);
        List<Member> members = super.list(wrapper);
        if (members.isEmpty()) {
            return Collections.emptyList();
        }
        return convertMemberList(merchantNo, members);
    }

    @Override
    public PageVo<MemberListVo> page(long merchantNo, MemberQuery query) {
        Page<Member> tempQuery = new Page<>(query.getPageIndex(), query.getPageSize());

        List<String> ids = new ArrayList<>();
        if (CollUtil.isNotEmpty(query.getTagIds())) {
            List<MemberTagMap> tagMaps = memberTagMapService.findAllByTagIds(query.getTagIds());
            ids = tagMaps.stream().map(MemberTagMap::getMemberId).distinct().collect(Collectors.toList());
        }
        Wrapper<Member> wrapper = new LambdaQueryWrapper<Member>()
                .eq(Member::getMerchantNo, merchantNo)
                .like(StrUtil.isNotBlank(query.getName()), Member::getName, query.getName())
                .like(StrUtil.isNotBlank(query.getPhone()), Member::getPhone, query.getPhone())
                .eq(query.getStoreNo() != null, Member::getStoreNo, query.getStoreNo())
                .in(CollUtil.isNotEmpty(ids), Member::getId, ids)
                .orderByDesc(Member::getCreateTime);
        IPage<Member> page = super.page(tempQuery, wrapper);
        List<MemberListVo> vos = convertMemberList(merchantNo, page.getRecords());
        return PageVo.result(page, vos);
    }

    @Override
    public List<MemberListVo> memberSearch(long merchantNo, String searchKey) {
        Wrapper<Member> wrapper = new LambdaQueryWrapper<Member>()
                .eq(Member::getMerchantNo, merchantNo)
                .and(StrUtil.isNotBlank(searchKey), x -> x
                        .like(Member::getName, searchKey)
                        .or()
                        .like(Member::getPhone, searchKey)
                );

        List<Member> members = super.list(wrapper);
        return convertMemberList(merchantNo, members);
    }

    @Override
    public List<Member> summaryList(MemberSummaryQuery query) {
        if (query.getEndDate() != null) {
            query.setEndDate(query.getEndDate().plusDays(1));
        }
        Wrapper<Member> wrapper = new LambdaQueryWrapper<Member>()
                .eq(Member::getMerchantNo, query.getMerchantNo())
                .eq(query.getStoreNo() != null, Member::getStoreNo, query.getStoreNo())
                .ge(query.getStartDate() != null, Member::getCreateTime, query.getStartDate())
                .lt(query.getEndDate() != null, Member::getCreateTime, query.getEndDate())
                .orderByDesc(Member::getCreateTime);
        return super.list(wrapper);
    }

    @Override
    public List<MemberRankVo> rank(long merchantNo) {
        Wrapper<Member> wrapper = new LambdaQueryWrapper<Member>()
                .eq(Member::getMerchantNo, merchantNo)
                .eq(Member::getStatus, CommonStatus.ENABLED.getCode());
        List<Member> list = super.list(wrapper);
        return list.stream().map(x -> {
                    MemberRankVo vo = new MemberRankVo();
                    BeanUtils.copyProperties(x, vo);
                    MemberCashierSummaryVo summaryVo = memberCashierRemote.getMemberCashierSummary(merchantNo, x.getId());
                    if (summaryVo != null) {
                        vo.setTotal(summaryVo.getOrderTotal());
                        vo.setAmount(summaryVo.getOrderAmount());
                    }
                    return vo;
                })
                .sorted(Comparator.comparing(MemberRankVo::getAmount).reversed())
                .limit(10)
                .collect(Collectors.toList());
    }

    private List<MemberListVo> convertMemberList(long merchantNo, List<Member> members) {
        List<String> ids = members.stream().map(Member::getId).collect(Collectors.toList());
        List<String> referenceIds = members.stream().map(Member::getReferenceId).distinct().collect(Collectors.toList());
        List<MerchantStore> stores = merchantStoreService.findAllByMerchantNo(merchantNo);
        List<MemberAssets> assets = memberAssetsService.findListByIds(ids);
        List<MemberLevel> levels = memberLevelService.findAllByMerchantNo(merchantNo);
        List<UserVo> users = clerkAccountService.findAllByIds(merchantNo, referenceIds);

        return members.stream().map(entity -> {
            MemberListVo vo = new MemberListVo();
            BeanUtils.copyProperties(entity, vo);
            stores.stream().filter(x -> Objects.equals(x.getStoreNo(), entity.getStoreNo()))
                    .findFirst()
                    .ifPresent(x -> vo.setStoreName(x.getStoreName()));

            assets.stream().filter(x -> x.getId().equals(entity.getId()))
                    .findFirst()
                    .ifPresent(memberAssets -> {
                        vo.setNowIntegral(memberAssets.getNowIntegral());
                        vo.setTotalIntegral(memberAssets.getAccrualIntegral());
                        vo.setNowRecharge(AmountUtil.parseBigDecimal(memberAssets.getNowRecharge()));
                        vo.setTotalRecharge(AmountUtil.parseBigDecimal(memberAssets.getAccrualRecharge()));
                    });
            List<MemberTagMapVo> tagMapVos = memberTagMapService.findAllByMemberId(entity.getMerchantNo(), entity.getId());
            vo.setTags(tagMapVos);
            List<MemberTimeCardListVo> timecardList = memberTimeCardService.findAllByMemberId(entity.getId());
            vo.setCounted(timecardList.size());
            levels.stream().filter(x -> x.getId().equals(entity.getLevelId()))
                    .findFirst()
                    .ifPresent(level -> vo.setLevelName(level.getName()));

            MemberCashierLastOrderVo cashierOrder = memberCashierRemote.getMemberCashierLastOrder(entity.getMerchantNo(), entity.getId());
            if (cashierOrder != null) {
                vo.setLastCashierTime(cashierOrder.getLastCashierTime());
                vo.setLastCashierStoreNo(cashierOrder.getStoreNo());
                vo.setLastCashierStoreName(cashierOrder.getStoreName());
            }
            users.stream().filter(user -> user.getId().equals(vo.getReferenceId()))
                    .findFirst()
                    .ifPresent(user -> vo.setReferenceName(user.getName()));
            return vo;
        }).collect(Collectors.toList());
    }

    private MemberInfoVo convertMemberInfo(Member entity) {
        MemberInfoVo vo = new MemberInfoVo();
        BeanUtils.copyProperties(entity, vo);
        vo.setStoreName(getStoreName(entity.getMerchantNo(), entity.getStoreNo()));
        MemberAssets memberAssets = memberAssetsService.getById(entity.getId());
        if (memberAssets != null) {
            vo.setNowIntegral(memberAssets.getNowIntegral());
            vo.setTotalIntegral(memberAssets.getAccrualIntegral());
            vo.setNowRecharge(AmountUtil.parseBigDecimal(memberAssets.getNowRecharge()));
            vo.setTotalRecharge(AmountUtil.parseBigDecimal(memberAssets.getAccrualRecharge()));
        }
        List<MemberTagMapVo> tagMapVos = memberTagMapService.findAllByMemberId(entity.getMerchantNo(), entity.getId());
        vo.setTags(tagMapVos);
        List<MemberTimeCardListVo> timecardList = memberTimeCardService.findAllByMemberId(entity.getId());
        vo.setCounted(timecardList.size());

        List<MemberLevel> levels = memberLevelService.findAllByMerchantNo(entity.getMerchantNo());
        levels.stream().filter(x -> x.getId().equals(entity.getLevelId()))
                .findFirst()
                .ifPresent(level -> vo.setLevelName(level.getName()));
        MemberCashierLastOrderVo cashierOrder = memberCashierRemote.getMemberCashierLastOrder(entity.getMerchantNo(), entity.getId());
        if (cashierOrder != null) {
            vo.setLastCashierTime(cashierOrder.getLastCashierTime());
            vo.setLastCashierStoreNo(cashierOrder.getStoreNo());
            vo.setLastCashierStoreName(cashierOrder.getStoreName());
        }
        ClerkAccount user = clerkAccountService.getById(vo.getReferenceId());
        if (user != null) {
            vo.setReferenceName(user.getName());
        }
        return vo;
    }

    private String getStoreName(long merchantNo, long storeNo) {
        MerchantStoreVo store = merchantStoreService.findByStoreNo(merchantNo, storeNo);
        if (store == null) {
            return null;
        }
        return store.getStoreName();
    }
}
